Skip to content

refactor(web): DRY orderedProfiles, fix circular imports, consolidate Profile type (LUM-2072)#33121

Merged
vex-assistant-bot[bot] merged 3 commits into
mainfrom
devin/1780436648-lum-2072-quick-wins
Jun 2, 2026
Merged

refactor(web): DRY orderedProfiles, fix circular imports, consolidate Profile type (LUM-2072)#33121
vex-assistant-bot[bot] merged 3 commits into
mainfrom
devin/1780436648-lum-2072-quick-wins

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Why

Five architectural issues in the settings/ai domain identified during the LUM-2072 first-principles audit:

  1. Duplicated ordering logic — The same "merge profileOrder with extras not in order" derivation was copy-pasted across language-model-card.tsx, call-site-overrides-modal.tsx, and manage-profiles-modal.tsx. A single utility function eliminates the duplication and ensures ordering semantics stay consistent.

  2. Circular importCallSiteOverrideDraft was defined in call-site-overrides-modal.tsx (a component file), then imported by ai-types.ts and use-daemon-config.ts. Types should live in the types module, not in a component that then becomes an implicit dependency for unrelated modules.

  3. Redundant type + mappermanage-profiles-modal.tsx defined a Profile interface that was identical to { name: string } & ProfileEntry, plus a profileEntryToProfile mapper that manually copied every field. The separate type was imported by profile-editor-modal.tsx, creating a cross-component type dependency for no reason.

  4. Incomplete consumer migration — After adding orderedProfiles to the hook, manage-profiles-modal.tsx still called buildOrderedProfiles locally instead of using the precomputed value. Dead export CUSTOM_SENTINEL in call-site-overrides-modal.tsx was exported but never imported by any other file.

  5. Duplicate mutation instancesuseDaemonConfig() defined its own patchConfigMutation and putImageGenModelMutation via useMutation, structurally identical to the separately-exported useDaemonConfigMutation(). The convenience wrappers (patchDaemonConfig, setImageGenModelOnDaemon) already handle error toasts, lazy ID resolution, and Sentry capture — the internal useMutation wrappers only added onSettled invalidation, which is simpler as a finally block.

What changed

Commit 1 (foundation):

  • ai-utils.ts: New buildOrderedProfiles(profiles, profileOrder) pure function encapsulating the shared ordering logic.
  • use-daemon-config.ts: Returns orderedProfiles as a derived slice (via useMemo + buildOrderedProfiles).
  • ai-types.ts: CallSiteOverrideDraft defined here directly (no longer imported from modal). New ProfileWithName type alias replaces the duplicate Profile interface.
  • call-site-overrides-modal.tsx: Removed CallSiteOverrideDraft definition, uses orderedProfiles from hook.
  • language-model-card.tsx: Uses orderedProfiles from hook instead of local derivation.
  • manage-profiles-modal.tsx: Removed Profile interface and profileEntryToProfile mapper (~35 lines deleted).
  • profile-editor-modal.tsx: Imports ProfileWithName from ai-types.

Commit 2 (audit fixes):

  • manage-profiles-modal.tsx: Passes orderedProfiles from hook to inner component instead of recomputing locally.
  • call-site-overrides-modal.tsx: Removed unnecessary export on CUSTOM_SENTINEL (only used within file).
  • use-daemon-config.ts: Eliminated patchConfigMutation and putImageGenModelMutation useMutation hooks. patchDaemonConfig and setImageGenModelOnDaemon now call the SDK directly (configPatch, modelImagegenPut) with finally-based cache invalidation via invalidateConfig().

Net: +22 −56 (audit) on top of +56 −101 (foundation) across 7 files. Pure refactor — no logic changes, no new behavior.

Follow-up tickets created

Larger architectural items identified during the audit, filed as sub-issues of LUM-2072:

  • LUM-2184: Split useDaemonConfig god-hook into query hook + separate mutation helpers
  • LUM-2185: Add type safety to daemon config mutation body (replace Record<string, unknown>)
  • LUM-2186: Replace one-shot useEffect hydration with query-derived draft state in service cards

References

Prompt / plan

LUM-2072 quick-wins phase — first-principles audit identified 5 issues. All fixed here; 3 larger items ticketed.

Test plan

  • bunx tsc --noEmit — clean
  • bun run lint — 0 errors
  • bun test src/domains/settings/ai/ — 8/8 pass, 16 assertions

Closes LUM-2072

Link to Devin session: https://app.devin.ai/sessions/4b3b130bbf274e5487da3b4992883093
Requested by: @ashleeradka

@linear

linear Bot commented Jun 2, 2026

Copy link
Copy Markdown

LUM-2072

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

…, dead export, duplicate mutations

- manage-profiles-modal: pass orderedProfiles from hook instead of recomputing via buildOrderedProfiles
- call-site-overrides-modal: remove unnecessary CUSTOM_SENTINEL export (only used within file)
- use-daemon-config: eliminate duplicate patchConfigMutation and putImageGenModelMutation useMutation hooks — patchDaemonConfig and setImageGenModelOnDaemon now call SDK directly with finally-based cache invalidation
@ashleeradka

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@vex-assistant-bot vex-assistant-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✦ Vex Review — APPROVE

Quick-wins audit cleanup on domains/settings/ai/ from the LUM-2072 first-principles pass. Five categories, all mechanical, all earn their place.

Verification at HEAD 0d006e35 (post-merge)

Conflict resolved against main (which gained #33094 + #33113 since this branch was cut). New HEAD is a merge commit — re-verified the audit-commit semantics survived intact through the merge.

Codex 👍 at HEAD. Boss triggered 22:15 against 0d006e35; Codex 22:18: "Didn't find any major issues. Keep them coming!" + 👍. Pre-trigger gate held. Devin's "No Issues Found" was at sha 1 (predates audit + merge), but Codex covered the post-merge HEAD.

Five Fixes, All Earned

1. buildOrderedProfiles extracted (ai-utils.ts:24). Single pure function replaces three copies of "merge profileOrder with extras not in order" across language-model-card, call-site-overrides-modal, manage-profiles-modal. Doc-commented for stability semantics. useDaemonConfig exposes the derived slice via useMemo([profiles, profileOrder]) — N consumers now share one memoization, not three.

2. Circular import broken (ai-types.ts:7). CallSiteOverrideDraft moved out of call-site-overrides-modal.tsx (a component) into ai-types.ts (the types module). Was previously: component file owned the type → ai-types.ts imported from it → use-daemon-config.ts imported from ai-types. Types-from-components is exactly the cycle vector this fixes.

3. Redundant Profile type + mapper deleted (manage-profiles-modal.tsx −35 lines). Replaced with ProfileWithName = { name: string } & ProfileEntry intersection in ai-types.ts. Mapper profileEntryToProfile (manual field copy across 12 fields) gone — intersection types do this structurally. Cross-component type dependency from profile-editor-modalmanage-profiles-modal removed.

4. Incomplete migration tightened. manage-profiles-modal was calling buildOrderedProfiles locally instead of consuming the orderedProfiles slice the hook now exposes. Dead export on CUSTOM_SENTINEL (only used within file) removed. Mechanical hygiene the LUM-2072 follow-up arc was specifically auditing for.

5. Duplicate mutation wrappers eliminated (use-daemon-config.ts −30 lines). The hook previously defined patchConfigMutation and putImageGenModelMutation via useMutation, structurally identical to the separately-exported useDaemonConfigMutation(). The convenience wrappers (patchDaemonConfig, setImageGenModelOnDaemon) already owned error-toast + lazy ID resolution + Sentry capture. The useMutation wrappers only added onSettled invalidation — converted to finally { invalidateConfig(); } blocks. Verified at HEAD use-daemon-config.ts:128-150 + :152-174 — both functions resolve assistantId, call the SDK directly (configPatch/modelImagegenPut), invalidate in finally. The useMutation indirection wasn't earning its keep here; SDK-direct + finally is cleaner and removes the mutation-state surface the callers weren't reading.

Anti-pattern Grep

Diff scanned line-by-line for as casts, non-null assertions, || 0 fallbacks: zero hits. The only ! in the diff is profiles[name]! inside buildOrderedProfiles — justified by the .filter((name) => name in profiles) guard directly above. TS's in-narrowing doesn't carry through .map, so the assertion is correct.

Follow-up Tickets Filed (LUM-2072 sub-issues)

  • LUM-2184: Split useDaemonConfig god-hook into query hook + separate mutation helpers
  • LUM-2185: Add type safety to daemon config mutation body (replace Record<string, unknown>)
  • LUM-2186: Replace one-shot useEffect hydration with query-derived draft state in service cards

Good triage — these are the right separations to defer; useDaemonConfig is already doing more than the first refactor scoped for, and bundling the split into this PR would obscure the quick-wins. The audit commit's Record<string, unknown> mutation body (LUM-2185) is the only typing seam I'd push to land sooner.

Net

+76 / −155 across 7 files. Three architectural anti-patterns closed (duplicate derivation, circular import, redundant type), two cleanups (dead export, duplicate useMutation), zero behavior change, three sub-tickets filed for the deferred work. Test plan green. CI 7/7 green.

Ship it.

@vex-assistant-bot vex-assistant-bot Bot merged commit 283f7cd into main Jun 2, 2026
7 checks passed
@vex-assistant-bot vex-assistant-bot Bot deleted the devin/1780436648-lum-2072-quick-wins branch June 2, 2026 22:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant